#define LOG_TAG "awinic_audio_algo"
//#define LOG_NDEBUG 0
/*#define VERY_VERY_VERBOSE_LOGGING*/

#define AWINIC_DEBUG
#ifdef  AWINIC_DEBUG 
#define ALOGD  printf
#else
#define ALOGD(a...)  do{}while(0);
#endif
#define ALOGE  printf

#define _GNU_SOURCE
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<string.h>
//#include <cutils/log.h>
//#include <cutils/properties.h>
#include <math.h>
#include <sys/time.h>
#include "awinic_audio.h"


typedef struct awinic_mec_cfg{
   aw_algo_cfg_t *left_cfg ;
   aw_algo_cfg_t *right_cfg;
}aw_mec_cfg_t;

#define MAX(a,b)  (((a)>=(b)) ? (a) : (b))
#define MIN(a,b)  (((a)<=(b)) ? (a) : (b))
#define true 1
#define false 0

#define CFG_BUF_MAX 1024
#define CFG_BIN_NAME  "awinic_params.bin"

#define LOG2F_COE1 ((float)1.23149591368684)
#define LOG2F_COE2 ((float)-4.11852516267426)
#define LOG2F_COE3 ((float)6.02197014179219)
#define LOG2F_COE4 ((float)-3.13396450166353)
#define LOG2_10    ((float)0.3010299956639812)      //1/log2(10)


static void writePcm(char* file_name,void*buffer,uint32_t bytes)
{
      FILE* fp;
      fp = fopen(file_name,"ab");
      if(fp == NULL)
      {
         ALOGE("%s: Open File Failed \n",__func__);
         return;
      }
      fwrite(buffer,sizeof(char),bytes,fp);
      fclose(fp);
}

static inline float fast_log2f_approx(float input)
{
   float out_data = 0.0;
   float f;
   int   e;

   f = frexpf(fabsf(input),&e);

    //out_data = coe1*f*f*f + coe2*f*f + coe3*f + coe4 + e
    out_data  = LOG2F_COE1;
    out_data *= f;
    out_data += LOG2F_COE2;
    out_data *= f;
    out_data += LOG2F_COE3;
    out_data *= f;
    out_data += LOG2F_COE4 + e;

   return out_data;
}

static inline float log10f_approx(float value)
{
    return fast_log2f_approx(value)*LOG2_10;  //log10 = log2(x)/log2(10);
}

static float pow_10f(float value)
{
     return expf(2.302585092994*value);    //expf(log(10)*x)
}


static inline int32_t absoluteInt32(int32_t inData)
{
    return ((inData >=0)?(inData):(-inData));
}

#define LOW_BIT 15
#define LOW_BIT_MASK 0x00007fff
#define H_MUL_BIT 30  //15*2
#define SIG_BIT_MASK 0x80000000

static inline int32_t Multiply_Separate(int32_t src1,int32_t src2,int32_t Exp_Bit)
{
       int32_t HightSrc1,HightSrc2,LowSrc1,LowSrc2;
       int32_t HighMul,MidMul,LowMul;
       int64_t sum=0;

       HightSrc1 = src1 >> LOW_BIT;
       LowSrc1   = src1&LOW_BIT_MASK;

       HightSrc2 = src2 >> LOW_BIT;
       LowSrc2   = src2&LOW_BIT_MASK;

       HighMul  = HightSrc1*HightSrc2;
       MidMul   = HightSrc1*LowSrc2 + HightSrc2*LowSrc1;
       LowMul   = LowSrc1*LowSrc2;

       sum  = HighMul;
       sum  = sum << LOW_BIT;
       sum += MidMul;
       sum  = sum << LOW_BIT;
       sum += LowMul;

       if((sum<0)&&(sum > -(1<<Exp_Bit)))
          sum =0;
       else
          sum = sum >> Exp_Bit;

       return (int32_t)sum;
}

static int64_t Multiply_Separate64(int32_t src1,int32_t src2)
{
       int32_t HightSrc1,HightSrc2,LowSrc1,LowSrc2;
       int32_t HighMul,MidMul,LowMul;
       int64_t sum=0;

       HightSrc1 = src1 >> LOW_BIT;
       LowSrc1   = src1&LOW_BIT_MASK;

       HightSrc2 = src2 >> LOW_BIT;
       LowSrc2   = src2&LOW_BIT_MASK;

       HighMul  = HightSrc1*HightSrc2;
       MidMul   = HightSrc1*LowSrc2 + HightSrc2*LowSrc1;
       LowMul   = LowSrc1*LowSrc2;

       sum  = HighMul;
       sum  = sum << LOW_BIT;
       sum += MidMul;
       sum  = sum << LOW_BIT;
       sum += LowMul;

       return sum;
}

static uint32_t AudioDataSeparationRL(int32_t* src,int32_t*dst1,int32_t *dst2,uint32_t bytes)
{
    uint32_t cnt = bytes/sizeof(int32_t),i;
    cnt = cnt/2;
    if(cnt > BUFFER_MAX)
    {
        return 0;
    }
    for(i=0;i<cnt;i++)
    {
        dst1[i] = src[2*i];
        dst2[i] = src[2*i + 1];
    }
    
    return cnt;
}

static void AudioDataMergePcm(int32_t*dst,int32_t*src1,int32_t *src2,uint32_t cnt)
{
    uint32_t i;
    for(i=0;i<cnt;i++)
    {
        dst[i*2]   = src1[i];
        dst[i*2+1] = src2[i];
    }
}

static void AwinicInitGain(int32_t* in_data,int32_t init_gain,uint32_t words_cnt)
{
    uint32_t i;
    int64_t temp;

    for(i=0;i<words_cnt;i++)
    {
        temp = (int64_t)(init_gain*in_data[i]);
        
        if(temp<0 && temp>(-INIT_GAIN_EXP))
            in_data[i] = 0;
        else
            in_data[i] = temp >> INIT_GAIN_EXP_BIT;

    }

}
static void AudioCompressTo18Bit(int32_t *data,uint32_t words_cnt)
{
    uint32_t i;

    for(i=0;i<words_cnt;i++)
    {
        if(data[i]<0 && data[i]> (-DAT_CPR))
           data[i] = 0;
        else
           data[i] = data[i] >> DAT_CPR_BIT;
    }
}
static void AwinicScaleGain(int32_t*data,int32_t gain,uint32_t words_cnt)
{
    uint32_t i;
    int64_t temp;

    for(i=0;i<words_cnt;i++)
    {
        temp = (int64_t)gain*data[i];

        if(temp <0 && temp > (-COE_EXP))
           data[i] = 0;
        else
           data[i] = temp >> COE_EXP_BIT;
    }
}

static void AwinicExGainProcess(int32_t*data,int32_t gain,uint32_t words_cnt)
{
    uint32_t i;
    int64_t temp;

    for(i=0;i<words_cnt;i++)
    {
        temp = (int64_t)gain*data[i];
        if(temp < 0 && temp > (-EX_GAIN_EXP))
           data[i] = 0;
        else
           data[i] = temp >> EX_GAIN_EXP_BIT;
    }
}

static void FilterProcess(int32_t* data,ft_cfg_t *cfg,uint32_t words_cnt)
{
    
   int64_t tmp_data[FT_COE_NUM];
   int64_t tmp_input,tmp_output;
   ft_coe_t coe = cfg->coe;
   ft_dat_t *store_data = &cfg->data;
   uint32_t i;

   // data process
   for(i=0;i<words_cnt;i++)
   {
      tmp_input = data[i];

      //y[n]=b0*a[n] + b1*a[n-1] + b2*a[n-2] + a1*y[n-1] + a2*y[n-2];
      tmp_data[0]=(tmp_input * coe.index[0]);
      tmp_data[1]=((int64_t)store_data->indata[0]  * coe.index[1]);
      tmp_data[2]=((int64_t)store_data->indata[1]  * coe.index[2]);
      tmp_data[3]=((int64_t)store_data->outdata[0] * coe.index[3]);
      tmp_data[4]=((int64_t)store_data->outdata[1] * coe.index[4]);
 
      tmp_output = tmp_data[0] + tmp_data[1] + tmp_data[2] + tmp_data[3] + tmp_data[4];

      if(tmp_output<0&&tmp_output> (COE_EXP_NEG))
           tmp_output = 0;
      else
           tmp_output = tmp_output >> (COE_EXP_BIT);     // out_data / COE_EXP

      data[i] = tmp_output;

      store_data->indata[1] = store_data->indata[0];
      store_data->indata[0] = tmp_input;

      store_data->outdata[1] = store_data->outdata[0];
      store_data->outdata[0] = tmp_output;
   }
}

static void AwinicExalgoProcess(int32_t* data,ex_cfg_t *cfg,uint32_t words_cnt)
{
    uint32_t i;
    AwinicExGainProcess(data,cfg->gain,words_cnt);
    
    for(i=0;i<FT_NUM;i++)
    {
        FilterProcess(data,&cfg->ft_cfg[i],words_cnt);
    }
}
static uint16_t AwinicMemDecimateProcess(int32_t*data,Mem_Cfg_t *cfg,uint32_t words_cnt,uint32_t enable)
{
    uint32_t i,decCnt = 0;

    if(DEC_ENABLE != (enable&DEC_ENABLE_MASK))
        return words_cnt;

    AwinicExGainProcess(data,cfg->dec_cfg.gain,words_cnt);
    for(i=0;i<DEC_NUM;i++)
    {
        FilterProcess(data,&cfg->dec_cfg.ft_cfg[i],words_cnt);
    }

    writePcm("./dec_filter.pcm",(char*)data,words_cnt*sizeof(int32_t));
    decCnt = words_cnt/DEC_NUMBER;
    if(words_cnt%DEC_NUMBER !=0)
        decCnt++;
    for(i=0;i<decCnt;i++)
    {
        data[i] = data[i*DEC_NUMBER];
    }
    //writePcm("./after_dec.pcm",(char*)data,decCnt*sizeof(int32_t));
    return decCnt;
}

static void AwinicPDalgoProcess(int32_t* data,pd_cfg_t *cfg,uint32_t words_cnt)
{
    
    uint32_t i;
    int64_t tmp_data;

    for(i=0;i<words_cnt;i++)
    {
        data[i] = llabs(data[i]);    //convert to absolute value
         if(cfg->data.data_count%cfg->coe.Hold_Rel == 0)
        {
             cfg->data.comb = cfg->data.section_peak;
             cfg->data.section_peak = 0;
             cfg->data.data_count = 1;
        }else{
            if(cfg->data.section_peak < data[i]){
                cfg->data.section_peak = data[i];
            }
                cfg->data.data_count++;
        }
        if(data[i] > cfg->data.comb)
            cfg->data.comb = data[i];
         data[i] = cfg->data.comb;
    }
}
static void AwinicGainComputerProcess(int32_t*data,gainff_cfg_t* cfg,uint32_t words_cnt)
{
   int32_t input_data,overshoot,out_data=0;
   int32_t w2 = absoluteInt32(cfg->coe.Width);
   uint32_t i;
   //ALOGD("%s: mode %d \n",__func__,cfg->mode);
   switch(cfg->mode){
        case COMPRESS:                  //compressor limiter
           for(i=0;i<words_cnt;i++)
           {
               input_data = data[i];
               //  20*log(input_data/2^24)   v ---> DB
               input_data = MAX(input_data,Min_Limit);
               input_data = (GainFF_Coe*(log10f_approx(input_data) - GainFF_Sub));
               //printf(" log2^18 %.15f  \n",log10f_approx(262114)); 
               // Subtrace threshold form input to get the amount of overshoot
               overshoot = input_data - cfg->coe.threshold;
               //rect = inTransition .* (a * (overshoot + w2) .^ 2) + (1 - inTransition) .* max(overshoot, 0);
               if(absoluteInt32(overshoot) >= w2){
                   out_data = MAX(overshoot,0);
               }else{
		           input_data = overshoot + cfg->coe.Width;
                   input_data = Multiply_Separate(input_data,input_data,GainFF_EXP_BIT);
			       out_data   = Multiply_Separate(cfg->coe.GainCoe,input_data,GainFF_EXP_BIT);
		       }
               //multiply rectifier overshoot with slope
	           //out_data = Multiply_Separate(out_data,cfg->coe.Slope,GainFF_EXP_BIT);
	           data[i] = out_data;
               //printf("%s:data %d \n",__func__,data[i]);
           }
           break;
        case EXPANDER:                  //expander
           for(i=0;i<words_cnt;i++)
           {
               input_data = data[i];
               //  20*log(input_data/2^27)   v ---> DB
               input_data = MAX(input_data,Min_Limit);
               input_data = (GainFF_Coe*(log10f_approx(input_data) - GainFF_Sub));
               // Subtrace threshold form input to get the amount of overshoot
               overshoot = input_data - cfg->coe.threshold;
               //rect = inTransition .* (a * (overshoot - w2) .^ 2) + (1 - inTransition) .* min(overshoot, 0);
		       if(absoluteInt32(overshoot)>=w2){
			       out_data = MIN(overshoot,0);
               }else{
			       input_data = overshoot - cfg->coe.Width;
			       input_data = Multiply_Separate(input_data,input_data,GainFF_EXP_BIT);
                   out_data = Multiply_Separate(cfg->coe.GainCoe,input_data,GainFF_EXP_BIT);
		       }
               //multiply rectifier overshoot with slope
	           //out_data = Multiply_Separate(out_data,cfg->coe.Slope,GainFF_EXP_BIT);
	           data[i] = out_data;
           }
           break;
        case NOISEGAT:                  //Noise Gate
           for(i=0;i<words_cnt;i++)
           {
              //out_data = 0;
              data[i] = 0;
           }
           break;
        case HARDHALF:                  //hard halfware rectifier
           for(i=0;i<words_cnt;i++)
           {
               input_data = data[i];
               //  20*log(input_data/2^27)   v ---> DB
               input_data = MAX(input_data,Min_Limit);
               input_data = (GainFF_Coe*(log10f_approx(input_data) - GainFF_Sub));
               // Subtrace threshold form input to get the amount of overshoot
               overshoot = input_data - cfg->coe.threshold;
               out_data  = MAX(overshoot,0);
               data[i] = out_data;
           }
           break;
        default:
            ALOGE("%s: flag is not match \n",__func__);
    }

    for(i=0;i<words_cnt;i++)
    {
        if(cfg->coe.ratio>50)
        {
            data[i] = -data[i];
        }else{

	        data[i] = Multiply_Separate(data[i],cfg->coe.Slope,GainFF_EXP_BIT);
        }
    }
}

static void AwinicGainSmoothProcess(int32_t*data,gs_cfg_t *cfg,uint32_t words_cnt)
{
    int32_t input_data,out_data,tmp,tmp1,tmp2;
	uint32_t i;
	float gain;
 
	for(i=0;i<words_cnt;i++)
	{
       input_data = data[i];

	   input_data = -input_data;

       if(input_data > cfg->data.state)
	   {
          cfg->data.state = Multiply_Separate(cfg->coe.AlphaAttr , cfg->data.state,GainFF_EXP_BIT) +
                                Multiply_Separate(cfg->data.beta,input_data,GainFF_EXP_BIT);
           if(cfg->data.state>input_data)
               cfg->data.state = input_data;
	   }else{
	      cfg->data.state = cfg->data.state - cfg->coe.AlphaRel;
        if(cfg->data.state<input_data)
           cfg->data.state = input_data;
	   }
         //ALOGD(" %s: attr=  %d rel= %d\n",__func__,cfg->coe.AlphaAttr,cfg->coe.AlphaRel);

       out_data = -cfg->data.state;
	   if(out_data !=0){
            gain = pow_10f(CONVERT_FLOAT*out_data);     //10^(out_data/20) ===> DB ==> int
            data[i] = gain*GS_DAT_EXP;
        }else{
            //gain = 1;
            data[i] = GS_DAT_MAX;;
        }
	}
    
}
static void MemExLimiterProcess(int32_t* data,Mem_Cfg_t* cfg,uint32_t words_cnt)
{
    AwinicExalgoProcess(data,&cfg->ex_cfg,words_cnt);
    //writePcm("/sdcard/mtklog/audio_dump/ex.pcm",(char*)data,sizeof(int32_t)*words_cnt);
    AwinicPDalgoProcess(data,&cfg->pd_cfg,words_cnt);
    //writePcm("/sdcard/mtklog/audio_dump/pd.pcm",(char*)data,sizeof(int32_t)*words_cnt);
    AwinicGainComputerProcess(data,&cfg->gain_cfg,words_cnt);
    //writePcm("/sdcard/mtklog/audio_dump/gf.pcm",(char*)data,sizeof(int32_t)*words_cnt);
    AwinicGainSmoothProcess(data,&cfg->gs_cfg,words_cnt);
    //writePcm("/sdcard/mtklog/audio_dump/gs.pcm",(char*)data,sizeof(int32_t)*words_cnt);
}
static void VolLimiterProcess(int32_t*data,Vol_Cfg_t *cfg,uint32_t words_cnt)
{
    AwinicPDalgoProcess(data,&cfg->pd_cfg,words_cnt);
    //writePcm("/sdcard/mtklog/audio_dump/vol_pd.pcm",(char*)data,sizeof(int32_t)*words_cnt);
    AwinicGainComputerProcess(data,&cfg->gain_cfg,words_cnt);
    //writePcm("/sdcard/mtklog/audio_dump/vol_gf.pcm",(char*)data,sizeof(int32_t)*words_cnt);
    AwinicGainSmoothProcess(data,&cfg->gs_cfg,words_cnt);
    //writePcm("/sdcard/mtklog/audio_dump/vol_gs.pcm",(char*)data,sizeof(int32_t)*words_cnt);
}
static void AwinicGainMin(int32_t* data1,int32_t *data2,uint32_t words_cnt)
{
    uint32_t i;

    for(i=0;i<words_cnt;i++)
    {
        data1[i]= MIN(data1[i],data2[i]);
    }
}
static void AwinicDataMpyGain(aw_algo_cfg_t*cfg,int32_t *gain,uint32_t words_cnt)
{
    int32_t *shift_buffer = cfg->shift_buffer;
    uint32_t      Shift_Cnt    = cfg->Shift_Num;
    uint32_t i,count1;
    int32_t *data_buffer = cfg->temp_buffer;
    int32_t *tmp_gain=gain;
    int64_t data;

    #if 0
    for(i=0;i<words_cnt;i++)
    {
        data = (int64_t)tmp_gain[i]*data_buffer[i];
      if(data < 0 && data > (int64_t)(-GS_DAT_EXP))
        data_buffer[i] = 0;
      else
        data_buffer[i] =(int32_t)(data >> GS_DAT_EXP_BIT);

    }
    return ;
    #endif


    if(words_cnt>= Shift_Cnt)
    {
       //Shift Buffer First Process
       for(i=0;i<Shift_Cnt;i++)
       {
           data = (int64_t)tmp_gain[i]*shift_buffer[i];
           if(data<0 && data>(int64_t)(-GS_DAT_EXP))
              tmp_gain[i] = 0;
           else
              tmp_gain[i] = data >> GS_DAT_EXP_BIT;
       }
       tmp_gain = tmp_gain + Shift_Cnt;
       count1 = words_cnt-Shift_Cnt;
      
      //Current Data * remainderGain (words_cnt - Shift_Cnti)
        for(i=0;i<count1;i++)
        {
           data = (int64_t)tmp_gain[i]*data_buffer[i];
           if(data<0 && data>(int64_t)(-GS_DAT_EXP))
              tmp_gain[i] = 0;
           else
              tmp_gain[i] = data >> GS_DAT_EXP_BIT;
        }
        //Move remainder Current Data to Shift Buffer
        data_buffer = data_buffer + (words_cnt-Shift_Cnt);
        for(i=0;i<Shift_Cnt;i++)
        {
            shift_buffer[i] = data_buffer[i];
        }
        //Move result to outBuffer
        data_buffer = cfg->temp_buffer;
        for(i=0;i<words_cnt;i++)
        {
            data_buffer[i] = gain[i]; 
        }
    }else{ 
        for(i=0;i<words_cnt;i++)
        {
            //shift_buffer Data Process First
            data = (int64_t)tmp_gain[i]*shift_buffer[i];
           if(data<0 && data>(int64_t)(-GS_DAT_EXP))
              tmp_gain[i] = 0;
           else
              tmp_gain[i] = data >> GS_DAT_EXP_BIT;
        }
        //Remainder Shift Data move Left
        count1 = Shift_Cnt -words_cnt;
        for(i=0;i<count1;i++)
        {
            shift_buffer[i] = shift_buffer[i+words_cnt];
        }
        //Move data_buffer to shift_buffer ,Move result to data_buffer
        shift_buffer = shift_buffer + (count1);
        for(i=0;i<words_cnt;i++)
        {
            shift_buffer[i] = data_buffer[i];
            data_buffer[i]  = tmp_gain[i];
        }
    }
}
static void AwinicDataMpyInitGain(int32_t*data,int32_t gain,uint32_t words_cnt)
{
    uint32_t i;
    int64_t temp;
    for(i=0;i<words_cnt;i++)
    {
        temp = (int64_t)data[i]*gain;

        if(temp <0 && temp > (-INIT_GAIN_EXP))
           data[i] = 0;
        else
           data[i] = temp >> INIT_GAIN_EXP_BIT;
    }
}
static uint16_t AwinicVolDecimateProcess(int32_t*data,uint32_t words_cnt,uint32_t enable)
{
    uint32_t i,j,ret=0;
    uint32_t count;

    if(DEC_ENABLE != (enable&DEC_ENABLE_MASK))
         return words_cnt; 

    for(i=0;i<words_cnt;i++)
    {
         data[i] = absoluteInt32(data[i]);
    }

    if(words_cnt%DEC_NUMBER == 0)
    {
        count = words_cnt/DEC_NUMBER;
        for(i=0;i<count;i++)
        {
            data[i] = data[i*DEC_NUMBER];
            for(j=0;j<DEC_NUMBER;j++)
            {
                data[i] = MAX(data[i],data[i*DEC_NUMBER+j]);
            }
        }
        ret = count;
    }else{
        count = words_cnt/DEC_NUMBER;
        for(i=0;i<count;i++)
        {
            data[i] = data[i*DEC_NUMBER];
            for(j=0;j<DEC_NUMBER;j++)
            {
                data[i] = MAX(data[i],data[i*DEC_NUMBER+j]);
            }
        }
        j=words_cnt - count*DEC_NUMBER;
        data[count] = data[count*DEC_NUMBER];
        for(i=0;i<j;j++)
        {
            data[count] = MAX(data[count],data[count*DEC_NUMBER+j]);
        }
        ret = count+1;
    }

    return ret;
}
static void AwinicUnDecimate(int32_t*inData,int32_t*outData,uint32_t inWordCnt,uint32_t outWordCnt)
{
    uint32_t i,j;
    uint32_t count;
    if(outWordCnt == inWordCnt)
    {
        for(i=0;i<outWordCnt;i++)
        {
            outData[i] = inData[i];
        }
        return;
    }

    if(outWordCnt%inWordCnt == 0)
    {
        //printf("%s: %d  %d\n",__func__,inWordCnt,outWordCnt);
        for(i=0;i<inWordCnt;i++)
        {
            for(j=0;j<DEC_NUMBER;j++)
            {
                outData[i*DEC_NUMBER+j] = inData[i];
                //printf("%s: i %d j %d\n",__func__,i,i*DEC_NUMBER+j);
            }
        }
    }else{
       count = outWordCnt/DEC_NUMBER;
        for(i=0;i<count;i++)
        {
            for(j=0;j<DEC_NUMBER;j++)
            {
                outData[i*DEC_NUMBER+j] = inData[i];
            }
        }
        j= outWordCnt - count*DEC_NUMBER;
        for(i=0;i<j;i++)
        {
            outData[count*DEC_NUMBER+i]=inData[count];
        }
    }
}
static void AwinicAudioProcess(aw_algo_cfg_t* cfg,uint32_t words_cnt)
{
    int32_t mem_buffer[BUFFER_MAX];
    int32_t vol_buffer[BUFFER_MAX];
    uint16_t decCnt=0,volDecCnt=0;

    //Copy input_data to temp buffer
    memcpy(mem_buffer,cfg->temp_buffer,sizeof(int32_t)*words_cnt);
    //Compress 24Bit data to 18 Bit data
    AudioCompressTo18Bit(mem_buffer,words_cnt);
    //multiply InitGain
    AwinicInitGain(mem_buffer,cfg->init_gain,words_cnt);
    //multiply scale_gain
    AwinicScaleGain(mem_buffer,cfg->scale_gain,words_cnt);
    //Copy Data to vol_buffer
    memcpy(vol_buffer,mem_buffer,sizeof(int32_t)*words_cnt);
    
    //MemExLimiter Process 
    decCnt = AwinicMemDecimateProcess(mem_buffer,&cfg->mem_cfg,words_cnt,cfg->modenable);
    MemExLimiterProcess(mem_buffer,&cfg->mem_cfg,decCnt);
    //writePcm("./mem_gain.pcm",(char*)mem_buffer,decCnt*sizeof(int32_t));
    
    //Voltage limiter Process
    volDecCnt =AwinicVolDecimateProcess(vol_buffer,words_cnt,cfg->modenable);
    VolLimiterProcess(vol_buffer,&cfg->vol_cfg,volDecCnt);
    //writePcm("./vol_gain.pcm",(char*)vol_buffer,volDecCnt*sizeof(int32_t));
    
    //Get Gain After Process
    AwinicGainMin(mem_buffer,vol_buffer,volDecCnt);
    AwinicUnDecimate(mem_buffer,vol_buffer,volDecCnt,words_cnt);
    //writePcm("./awinic_min_gain.pcm",(char*)vol_buffer,words_cnt*sizeof(int32_t));

    //Data multiply Gain 
    AwinicDataMpyGain(cfg,vol_buffer,words_cnt);
    //writePcm("./awinic_mpyGain.pcm",(char*)cfg->temp_buffer,words_cnt*sizeof(int32_t));
    
    //OutData Multiply InitGain 
    AwinicDataMpyInitGain(cfg->temp_buffer,cfg->init_gain,words_cnt);
    //writePcm("/sdcard/mtklog/audio_dump/mpyinitgain.pcm",(char*)cfg->temp_buffer,words_cnt*sizeof(int32_t));
}


int32_t AwinicAudioHandle(void* buffer,uint32_t bytes,void*cfg_ptr)
{
   //ALOGD("%s: enter\n",__func__);

    if(buffer == NULL || cfg_ptr == NULL || bytes <=0)
    {
        ALOGE("%s:Error Pointer \n",__func__);
        return -1;
    }
    
    uint32_t words_cnt;

    aw_mec_cfg_t*mec_cfg = (aw_mec_cfg_t*)cfg_ptr;

    if(mec_cfg->left_cfg == NULL || mec_cfg->right_cfg == NULL)
    {
        ALOGE("%s:init cfg Pointer error \n",__func__);
        return -1;
    }
    //writePcm("/sdcard/mtklog/audio_dump/before.pcm",buffer,bytes); 
    words_cnt = AudioDataSeparationRL(buffer,mec_cfg->left_cfg->temp_buffer,mec_cfg->right_cfg->temp_buffer,bytes);
    //ALOGD("%s: AudioDataSeparationRL words_cnt %d\n",__func__,words_cnt);
    if(0==words_cnt )
    {
        ALOGE("%s: Input Data Overloader or Is Zero  \n",__func__);
        return -1;
    }
    AwinicAudioProcess(mec_cfg->left_cfg,words_cnt);
	 //ALOGD("%s: AwinicAudioProcess L words_cnt %d\n",__func__,words_cnt);
    //AwinicAudioProcess(mec_cfg->right_cfg,words_cnt);
	//ALOGD("%s: AwinicAudioProcess R words_cnt %d\n",__func__,words_cnt);

    AudioDataMergePcm((int32_t*)buffer,(int32_t*)&mec_cfg->left_cfg->temp_buffer,(int32_t*)&mec_cfg->right_cfg->temp_buffer,words_cnt);
	//ALOGD("%s: AudioDataMergePcm L words_cnt %d\n",__func__,words_cnt);
    //writePcm("/sdcard/mtklog/audio_dump/after.pcm",buffer,bytes);
    return 0;
}

static int AwinicAlgoReadCfg(char*path_name,void *buffer,uint32_t bytes)
{
    ALOGD("%s: enter \n",__func__);

    int fd,ret;
    fd = open(path_name,O_RDONLY);
    if(fd<0)
    {
        ALOGE("%s: open cfg firmware file failed\n",__func__);
        return -1;
    }

    ret = read(fd,buffer,bytes);
    if(ret<0)
    {
        ALOGE("%s: read cfg firmware file failed \n",__func__);
        close(fd);
        return -1;
    }
    close(fd);
#if 0
    int32_t *test = (int32_t*)buffer;
    uint32_t i;
    for(i=0;i<bytes/4;i++)
      ALOGD("%s: %d = %d\n",__func__,i,test[i]);
#endif
    return ret;
}
static int AwinicCfgInit(aw_read_cfg_t *coe_cfg,aw_algo_cfg_t *cfg)
{
    
    cfg->init_gain = coe_cfg->init_gain;
    cfg->scale_gain= coe_cfg->scale_gain;
    cfg->Shift_Num = coe_cfg->Shift_Num;
    cfg->modenable = coe_cfg->aw_algo_coe.modenable;

    cfg->mem_cfg.dec_cfg.gain = coe_cfg->aw_algo_coe.dec_coe.gain;
    cfg->mem_cfg.dec_cfg.ft_cfg[0].coe = coe_cfg->aw_algo_coe.dec_coe.ft_coe[0];
    cfg->mem_cfg.dec_cfg.ft_cfg[1].coe = coe_cfg->aw_algo_coe.dec_coe.ft_coe[1];

    cfg->mem_cfg.ex_cfg.gain = coe_cfg->aw_algo_coe.ex_coe.gain;
    cfg->mem_cfg.ex_cfg.ft_cfg[0].coe = coe_cfg->aw_algo_coe.ex_coe.ft_coe[0];
    cfg->mem_cfg.ex_cfg.ft_cfg[1].coe = coe_cfg->aw_algo_coe.ex_coe.ft_coe[1];
    cfg->mem_cfg.ex_cfg.ft_cfg[2].coe = coe_cfg->aw_algo_coe.ex_coe.ft_coe[2];
    cfg->mem_cfg.pd_cfg.data.section_peak = 0;
    cfg->mem_cfg.pd_cfg.data.data_count = 1;
    cfg->mem_cfg.pd_cfg.coe = coe_cfg->aw_algo_coe.pd_coe;
    cfg->mem_cfg.gain_cfg   = coe_cfg->aw_algo_coe.gain_cfg;
    cfg->mem_cfg.gain_cfg.coe.threshold = coe_cfg->xmax_threshold;
    cfg->mem_cfg.gs_cfg.coe = coe_cfg->aw_algo_coe.gs_coe;
    cfg->mem_cfg.gs_cfg.data.beta = ((BETA_CONSANT*GainFF_EXP) - (BETA_CONSANT*cfg->mem_cfg.gs_cfg.coe.AlphaAttr));

    cfg->vol_cfg.pd_cfg.coe = coe_cfg->aw_algo_coe.pd_coe;
    cfg->vol_cfg.pd_cfg.data.section_peak = 0;
    cfg->vol_cfg.pd_cfg.data.data_count = 1;
    cfg->vol_cfg.gain_cfg   = coe_cfg->aw_algo_coe.gain_cfg;
    cfg->vol_cfg.gain_cfg.coe.threshold   = coe_cfg->vmax_threshold;
    cfg->vol_cfg.gs_cfg.coe = coe_cfg->aw_algo_coe.gs_coe;
    cfg->vol_cfg.gs_cfg.data.beta = ((BETA_CONSANT*GainFF_EXP) - (BETA_CONSANT*cfg->vol_cfg.gs_cfg.coe.AlphaAttr));

    memset(cfg->shift_buffer,0,SHIFT_MAX*sizeof(int32_t));
    memset(cfg->temp_buffer,0,BUFFER_MAX*sizeof(int32_t));

    return 0;
}

uint32_t AwinicGetSize(void)
{
    uint32_t result;

    result  = 2*sizeof(aw_algo_cfg_t*);
    result += 2*sizeof(aw_algo_cfg_t);

    return result;
}

int AwinicAlgoInit(void* cfg_ptr)
{
    aw_mec_cfg_t *mec_cfg = (aw_mec_cfg_t*)cfg_ptr;
    char* ptr = (char*)cfg_ptr;
    ptr = ptr+2*sizeof(aw_algo_cfg_t*);
    mec_cfg->left_cfg     = (aw_algo_cfg_t*)(ptr);
    ptr = ptr + sizeof(aw_algo_cfg_t);
    mec_cfg->right_cfg    = (aw_algo_cfg_t*)(ptr);

    aw_read_cfg_t *read_cfg = NULL;
    read_cfg = (aw_read_cfg_t*)calloc(2,sizeof(aw_read_cfg_t));
    if(read_cfg == NULL)
    {
        ALOGE("%s: read cfg bad ptr \n",__func__);
        return -1;
    }

    int ret = AwinicAlgoReadCfg(CFG_BIN_NAME,(char*)read_cfg,2*sizeof(aw_read_cfg_t));
    if(ret != 2*sizeof(aw_read_cfg_t))
    {
        ALOGE("%s: read cfg file  bytes is %d ,needed bytes is %u\n ",__func__,ret,(unsigned int)(2*sizeof(aw_read_cfg_t)));
        free(read_cfg);
        read_cfg = NULL;
        return -1;
    }
    AwinicCfgInit(read_cfg,mec_cfg->left_cfg);
#if 0
    int i;
    int32_t *test = (int32_t*)mec_cfg->left_cfg;
    //int count = 100 ;//- SHIFT_MAX- BUFFER_MAX;
    //ALOGD("%s: count %d \n",count);
    for(i=0;i<(70);i++)
    {
       ALOGD("%s:aw_coe L num %d = %d \n",__func__,i,test[i]);
    }
#endif
    AwinicCfgInit(read_cfg+1,mec_cfg->right_cfg);
#if 0
    test = (int32_t*)mec_cfg->right_cfg;
    for(i=0;i<70;i++)
    {
       ALOGD("%s:aw_coe R num %d = %d \n",__func__,i,test[i]);
    }
#endif
    free(read_cfg);
    read_cfg = NULL;

    return 0;
}
static void AwinicCfgReset(aw_algo_cfg_t*cfg)
{
    memset(cfg->shift_buffer,0,SHIFT_MAX*sizeof(int32_t));
    memset(cfg->temp_buffer,0,BUFFER_MAX*sizeof(int32_t));
    

    Mem_Cfg_t* mem_cfg = &cfg->mem_cfg;
    int i,j;

    for(i=0;i<DEC_NUM;i++)
    {
       for(j=0;j < FT_DATA_NUM;j++)
      {
         mem_cfg->dec_cfg.ft_cfg[i].data.indata[j] = 0;
         mem_cfg->dec_cfg.ft_cfg[i].data.outdata[j] = 0;
      }

    }

    for(i=0;i<FT_NUM;i++)
    {
       for(j=0;j < FT_DATA_NUM;j++)
      {
         mem_cfg->ex_cfg.ft_cfg[i].data.indata[j] = 0;
         mem_cfg->ex_cfg.ft_cfg[i].data.outdata[j] = 0;
      }
    }

    mem_cfg->pd_cfg.data.comb = 0;
    mem_cfg->pd_cfg.data.section_peak = 0;
    mem_cfg->pd_cfg.data.data_count = 1;
    mem_cfg->pd_cfg.data.hold_cnt_att = 0;
    mem_cfg->pd_cfg.data.hold_cnt_rel = 0;

    mem_cfg->gs_cfg.data.state = 0;
    
    Vol_Cfg_t *vol_cfg = &cfg->vol_cfg;
    vol_cfg->pd_cfg.data.comb = 0;
    vol_cfg->pd_cfg.data.section_peak = 0;
    vol_cfg->pd_cfg.data.data_count = 1;
    vol_cfg->pd_cfg.data.hold_cnt_att = 0;
    vol_cfg->pd_cfg.data.hold_cnt_rel = 0;

}
void AwinicAlgoClear(void* cfg_ptr)
{
    aw_mec_cfg_t*mec_cfg = (aw_mec_cfg_t*)cfg_ptr;

    AwinicCfgReset(mec_cfg->left_cfg);
#if 0
    int i;
    int32_t *test = (int32_t*)mec_cfg->left_cfg;
    for(i=0;i<(70);i++)
    {
       ALOGD("%s:aw_coe L num %d = %d \n",__func__,i,test[i]);
    }
#endif
    AwinicCfgReset(mec_cfg->right_cfg);
#if 0
    test = (int32_t*)mec_cfg->right_cfg;
    for(i=0;i<(70);i++)
    {
       ALOGD("%s:aw_coe R num %d = %d \n",__func__,i,test[i]);
    }
#endif
}

void AwinicAlgoDeinit(void* cfg_ptr)
{
    aw_mec_cfg_t *mec_cfg = (aw_mec_cfg_t*)cfg_ptr;
    mec_cfg->left_cfg     = NULL;
    mec_cfg->right_cfg    = NULL;
}
